package jp.sourceforge.talisman.mds.cluster;

/*
 * $Id: HierarchicalClusteringMethod.java,v 1.1 2009/09/12 05:06:01 weiwei Exp $
 */

import java.util.LinkedList;
import java.util.List;

import jp.sourceforge.talisman.mds.Item;
import jp.sourceforge.talisman.mds.distance.ClusterDistanceCalculator;

/**
 * 
 * @author Haruaki Tamada
 * @version $Revision: 1.1 $ 
 */
class HierarchicalClusteringMethod extends AbstractClusteringMethod{
    public HierarchicalClusteringMethod(ClusteringParameter param) throws ParameterTypeMismatchException{
        super(param);
        if(!(param instanceof HierarchicalClusteringParameter)){
            throw new ParameterTypeMismatchException("HierarchicalClusteringParameter is required");
        }
    }

    public HierarchicalClusteringMethod(HierarchicalClusteringParameter param){
        super(param);
    }

    @Override
    protected Cluster[] doClustering(Item[] items, ClusteringParameter parameter){
        HierarchicalClusteringParameter param = (HierarchicalClusteringParameter)parameter;
        Cluster[] clusters = initClusters(items, param);
        List<Cluster> list = new LinkedList<Cluster>();
        for(Cluster c: clusters) list.add(c);

        ClusterDistanceCalculator cdc = param.getClusterDistanceCalculator();
        while(list.size() > param.getNumberOfCluster()){
            Cluster cluster1 = null, cluster2 = null;
            double distance = Double.MAX_VALUE;
            for(int i = 0; i < list.size(); i++){
                Cluster c1 = list.get(i);
                for(int j = i + 1; j < list.size(); j++){
                    Cluster c2 = list.get(j);
                    double d = cdc.calculate(c1, c2);
                    if(d < distance){
                        distance = d;
                        cluster1 = c1;
                        cluster2 = c2;
                    }
                }
            }
            if(cluster1 != null && cluster2 != null){
                list.remove(cluster1);
                list.remove(cluster2);
                Cluster c = new Cluster();
                c.setName(cluster1.getName() + ", " + cluster2.getName());
                for(Item item: cluster1) c.addItem(item);
                for(Item item: cluster2) c.addItem(item);
                list.add(c);
            }
        }

        return list.toArray(new Cluster[list.size()]);
    }

    private Cluster[] initClusters(Item[] items, HierarchicalClusteringParameter param){
        Cluster[] clusters = new Cluster[items.length];

        for(int i = 0; i < items.length; i++){
            clusters[i] = new Cluster();
            clusters[i].setName(items[i].getName());
            clusters[i].addItem(items[i]);
        }
        return clusters;
    }
}
